require "math"
require "factory"
require "heros"
require "os"
require "restrains"

math.randomseed(os.time())

local _tohit_base = 75
local _critical_base = 30

function isdead(heroi)
	return heroi.hp<=0
end

function fightair(heroi, flag)
	local str = "0"
	local rv = math.random(0, 100)
	local damage = 1
	for k, v in pairs(heroi.damage) do
		if rv< v.w then
			str = v.id
			damage = v.d
			break
		else
			rv = rv - v.w
		end
	end

	if flag and flag>1 then
		print ("damage:"..str, damage+heroi.ap , "=", damage, "+", heroi.ap)
	end

	return damage, heroi.ap
end

function fightair_each_lvl(heroc, equip, flag)
	print ("#########################################################")
	local cls_str = heros_name[heroc.cls]
	local sum_a = 0
	local sum_b = 0
	for lvl = 1, 80 do
		local heroi = create_hero_instance(heroc, lvl, equip)
		local b = 0
		local a = 0
		for i = 1,20 do
			local tb, ta
			tb, ta = fightair(heroi, flag)
			b = b+tb
			a = a+ta
		end
		if flag then
			print(cls_str, "lvl"..lvl,"damage", a/20 .."/".. (a+b)/20 , a*100/(a+b).."%")
		end
		sum_a = sum_a + a
		sum_b = sum_b + b
	end
	print(cls_str, "avg:","damage", sum_a/1600 .."/".. (sum_a+sum_b)/1600 , sum_a*100/(sum_a+sum_b).."%")
end

--
--	fight between heros in order to test their hit or miss
function fight_tohit(heroia, heroib, times, flag)
	if not (heroia and heroib) then
		print("Null hero instance!")
		return
	end
	local restrain = restrains[heroia.cls][heroib.cls]
	if not times then
		times = 100
	end
	local hit_times = 0
	for i = 1, times do
		if (math.random(0, 100) <= heroia.tohit * restrain.r_tohit * _tohit_base/ heroib.tododge / restrain.r_tododge) then
			hit_times=hit_times+1
		end
	end

	return hit_times
end

function fight_tohit_each_lvl(heroca, herocb, equip, flag)
	if not (heroca and herocb) then
		print("Null heroc!")
		return
	end
	print("**********************************************************")
	local vs_str = heroca.name .. " vs " .. herocb.name
	local sum_hits1 = 0
	local sum_hits2 = 0
	for lvl = 1, 80 do
		local heroia = create_hero_instance(heroca, lvl, equip)
		local heroib = create_hero_instance(herocb, lvl, equip)
		local hits1 = fight_tohit(heroia, heroib, 1000, flag)
		local hits2 = fight_tohit(heroib, heroia, 1000, flag)
		if flag then
			print(vs_str, "lvl:"..lvl, hits1/10 .. "% " .. hits2/10 .. "%")
		end
		sum_hits1 = sum_hits1 + hits1
		sum_hits2 = sum_hits2 + hits2
	end
	print(vs_str, "avg:", sum_hits1/800 .. "% " .. sum_hits2/800 .. "%")
end

function fight_critical(heroia, heroib, times, flag)
	if not (heroia and heroib) then
		print("Null hero instance!")
		return
	end
	local restrain = restrains[heroia.cls][heroib.cls]
	if not times then
		times = 100
	end
	local crt_times = 0
	for i = 1, times do
		if (math.random(0,100) <= heroia.c * restrain.r_c * _critical_base / heroib.t / restrain.r_t) then
			crt_times=crt_times+1
		end
	end

	return crt_times
end


--
--	fight heroia with heroib for only one turn
function fight(heroia, heroib, flag)
	if not (heroia and heroib) then
		print("Null hero instance!")
		return
	end

	local restrain = restrains[heroia.cls][heroib.cls]

	if (math.random(0, 100) <= heroia.tohit * restrain.r_tohit * _tohit_base/ heroib.tododge / restrain.r_tododge) then
		local str = "0"
		local rv = math.random(0, 100)
		local damage = 1
		for k, v in pairs(heroia.damage) do
			if rv< v.w then
				str = v.id
				damage = v.d
				break
			else
				rv = rv - v.w
			end
		end

		damage = damage + heroia.ap * restrain.r_ap

		local is_c = false
		if (math.random(0,100) <= heroia.c * restrain.r_c * _critical_base / heroib.t / restrain.r_t) then
			is_c = true
			damage = damage * heroia.critical
		end


		if is_c then
			str = str .. "/critical"
		end

		if damage<=heroib.dp then
			damage = 1
		else
			damage = damage - heroib.dp * restrain.r_dp
		end

		heroib.hp = heroib.hp - damage

		if flag then print("fight:", str, damage) end
	else
		if flag then print("fight:", "miss") end
	end
end

--
--	fight heroia with heroib, until one of them dead
function fight2death(heroia, heroib, flag)
	if not (heroia and heroib) then
		print("Null hero instance!")
		return
	end
	local result = true;
	if flag then
		print("######################################################")
		if flag>2 then
			show_hero_instance(heroia)
			show_hero_instance(heroib)
		end
		print("fight begins!")
	end

	for turn = 1,100 do
		if flag then print("turn", turn) end

		fight(heroia, heroib, flag)
		if flag and flag>1 then
			show_hero_instance(heroib)
		end

		if isdead(heroib) then
			if flag then print("dead b") end
			break
		end

		fight(heroib, heroia, flag)
		if flag and flag>1 then
			show_hero_instance(heroia)
		end

		if isdead(heroia) then
			if flag then print("dead a") end
			result = false
			break
		end
	end
	if flag then
		print("fight ends!")
		print("######################################################")
	end
	return result
end

--
--	fight heroia with heroib which aim at testing how many turns it take to ending the fight
function fight_turns(heroia, heroib)
	if not (heroia and heroib) then
		print("Null hero instance!")
		return
	end
	for turn = 1,100 do
		fight(heroia, heroib, flag)

		if isdead(heroib) then
			return turn
		end
		fight(heroib, heroia, flag)
		if isdead(heroia) then
			return turn
		end
	end
	return 100
end

function fight_turns_each_lvl(heroca, herocb, equip, flag)
	if not (heroca and herocb) then
		print("Null heroc!")
		return
	end
	print("**********************************************************")
	local sum_all = 0
	local vs_str = heroca.name .. " vs " .. herocb.name
	for lvl = 1, 80 do
		local heroia = create_hero_instance(heroca, lvl, equip)
		local heroib = create_hero_instance(herocb, lvl, equip)
		local sum = 0
		for i = 1, 100 do
			sum = sum + fight_turns(heroia, heroib)
			reset_hero_instance(heroia)
			reset_hero_instance(heroib)
		end
		if flag then
			print(vs_str, "lvl" .. lvl,  sum/100, "turns")
		end

		sum_all = sum_all + sum
	end
	print(vs_str, "avg."  , sum_all/8000, "turns")
end

function fight_each_lvl(heroca, herocb, equip, flag)
	if not (heroca and herocb) then
		print("Null heroc!")
		return
	end
	local sum_win = 0
	local sum_lose = 0
	local vs_str = heroca.name .. " vs " .. herocb.name
	for lvl = 1, 80 do
		local heroia = create_hero_instance(heroca, lvl, equip)
		local heroib = create_hero_instance(herocb, lvl, equip)
		local sum = 0
		for i = 1, 50 do
			if fight2death(heroia, heroib) then
				sum = sum + 1
			end
			reset_hero_instance(heroia)
			reset_hero_instance(heroib)
		end
		for i = 1, 50 do
			if not fight2death(heroib, heroia) then
				sum = sum + 1
			end
			reset_hero_instance(heroia)
			reset_hero_instance(heroib)
		end
		if flag then
			print(vs_str, "lvl" .. lvl,  sum .. ":" .. 100-sum)
		end
		sum_win = sum_win + sum
		sum_lose = sum_lose + 100-sum
	end
	print(vs_str, "avg."  , sum_win/80 .. ":" .. sum_lose/80)
end

--
--	monster fight heroc
--	each same lvl
--	same equip
--
function fight_each_lvl_m(monc, heroc, equip)
	if not monc then
		print("Null monsterc!")
		return
	end
	if not heroc then
		print("Null heroc!")
		return
	end
	local sum_win = 0
	local sum_lose = 0
	local vs_str = monc.name .. " vs " .. heroc.name
	for lvl = 1, 80 do
		local moni = create_monster_instance(monc, lvl, equip)
		local heroi = create_hero_instance(heroc, lvl, equip)
		local sum = 0
		for i = 1, 50 do
			if fight2death(moni, heroi) then
				sum = sum + 1
			end
			reset_hero_instance(moni)
			reset_hero_instance(heroi)
		end
		for i = 1, 50 do
			if not fight2death(heroi, moni) then
				sum = sum + 1
			end
			reset_hero_instance(heroi)
			reset_hero_instance(moni)
		end

		sum_win = sum_win + sum
		sum_lose = sum_lose + 100-sum
	end
	print(vs_str, "avg."  , sum_win/80 .. ":" .. sum_lose/80)
end

--
--	fight between a monster and a hero
--	different equip
--	different lvl
--	monster data from data
--
function fight_m(monc, heroc, lvl, equip)
	if not monc then
		print("Null monsterc!")
		return
	end
	if not heroc then
		print("Null heroc!")
		return
	end
	lvl = lvl or monc.lvl
	equip = equip or monc.equip

	local vs_str = monc.name .. " lvl:".. monc.lvl .." e:"..monc.equip .. " vs " .. heroc.name .. " lvl:" .. lvl .. " e:" .. equip

	local moni = create_monster_instance(monc)
	local heroi = create_hero_instance(heroc, lvl, equip)
	local sum = 0
	for i = 1, 200 do
		if fight2death(moni, heroi) then
			sum = sum + 1
		end
		reset_hero_instance(moni)
		reset_hero_instance(heroi)
	end
	for i = 1, 200 do
		if not fight2death(heroi, moni) then
			sum = sum + 1
		end
		reset_hero_instance(heroi)
		reset_hero_instance(moni)
	end
	print(vs_str, "avg."  , sum/4 .. ":" .. (400-sum)/4)
end
